package com.sinosoft.vaccinetoai.utils.retry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.atomic.AtomicReference;

/**
 * 重试工具
 * @author xzh
 * @date 2024-01-25 10:23
 * @since 1.0.0
 */
public class RetryUtil {

    private static final Logger log = LoggerFactory.getLogger(RetryUtil.class);

    /**
     * 回调结果检查
     */
    public interface ResultCheck {
        /**
         * 是否匹配
         *
         * @return 匹配结果
         */
        boolean matching();

        /**
         * 获取 结果
         *
         * @return Object
         */
        Object getResult();
    }

    /**
     * 在遇到异常时尝试重试
     *
     * @param retryLimit    重试次数
     * @param retryCallable 重试回调
     * @param <V>           泛型
     * @return V 结果
     */
    public static <V extends ResultCheck> V retryOnException(int retryLimit,
                                                             java.util.concurrent.Callable<V> retryCallable) {
        V v = null;
        for (int i = 0; i < retryLimit; i++) {
            try {
                v = retryCallable.call();
            } catch (Exception e) {
                log.warn("retry on " + (i + 1) + " times v = " + (v == null ? null : v.getResult()), e);
            }
            if (null != v && v.matching()) break;
            log.error("retry on " + (i + 1) + " times but not matching v = " + (v == null ? null : v.getResult()));
        }
        return v;
    }

    /**
     * 在遇到异常时尝试重试
     *
     * @param retryLimit    重试次数
     * @param sleepMillis   每次重试之后休眠的时间
     * @param retryCallable 重试回调
     * @param <V>           泛型
     * @return V 结果
     * @throws InterruptedException 线程异常
     */
    public static <V extends ResultCheck> V retryOnException(int retryLimit, long sleepMillis,
                                                             java.util.concurrent.Callable<V> retryCallable) throws InterruptedException {
        V v = null;
        for (int i = 0; i < retryLimit; i++) {
            try {
                v = retryCallable.call();
            } catch (Exception e) {
                log.warn("retry on " + (i + 1) + " times v = " + (v == null ? null : v.getResult()), e);
            }
            if (null != v && v.matching()) {
                break;
            }
            log.error("retry on " + (i + 1) + " times but not matching v = " + (v == null ? null : v.getResult()));
            if (sleepMillis > 0) {
                Thread.sleep(sleepMillis);
            }
        }
        return v;
    }

    /**
     * 重试任务 安全重试（解决闭包）
     * @param retryableTask 重试任务的执行
     * @param <T>
     * @return
     * @throws InterruptedException
     */
    public static <T> T asyncRetry(RetryableTask<T> retryableTask,Integer retryLimit,Long sleepTime) throws InterruptedException {
        AtomicReference<T> resultRef = new AtomicReference<>();

        // 使用RetryUtil进行重试
        RetryUtil.retryOnException(retryLimit, sleepTime, () -> {
            T result = (T) retryableTask.execute();
            resultRef.set(result); //

            return new ResultCheck() {
                @Override
                public boolean matching() {
                    return result != null;
                }

                @Override
                public Object getResult() {
                    return null;
                }
            };
        });

        return resultRef.get(); // 从AtomicReference获取最终结果

    }


}
